/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jbrowser.pacman.services;

import jbrowser.pacman.demo.util.ConsoleDependencyGraphDumper;
import jbrowser.pacman.demo.util.Pacman;
import java.io.File;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import java.util.jar.Attributes;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.login.LoginException;
import jbrowser.services.ClassService;
import jbrowser.services.SecurityManagerService;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.collection.CollectResult;
import org.sonatype.aether.collection.DependencyCollectionException;
import org.sonatype.aether.graph.Dependency;
import org.sonatype.aether.graph.DependencyNode;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.resolution.VersionRangeRequest;
import org.sonatype.aether.resolution.VersionRangeResult;
import org.sonatype.aether.util.artifact.DefaultArtifact;
import org.sonatype.aether.version.Version;

/**
 *
 * @author suse
 */
public class MavenClassService implements ClassService {

    private String artifactName;
    private URL jarFileURL;
    private URLClassLoader classLoader;

    private Version findNewestVersion(String coords) throws Exception {
        RepositorySystem system = Pacman.newRepositorySystem();
        RepositorySystemSession session = Pacman.newRepositorySystemSession(system);
        Artifact artifact = new DefaultArtifact(coords + ":[0,)");
        RemoteRepository repo = Pacman.newCentralRepository();
        VersionRangeRequest rangeRequest = new VersionRangeRequest();
        rangeRequest.setArtifact(artifact);
        //  rangeRequest.addRepository(repo);
        VersionRangeResult rangeResult = system.resolveVersionRange(session, rangeRequest);
        Version newestVersion = rangeResult.getHighestVersion();
        System.out.println("Newest version " + newestVersion + " from repository " + rangeResult.getRepository(newestVersion));
        return newestVersion;
    }

    private List<URL> getDependencyURLs(String coords) throws DependencyCollectionException {
        RepositorySystem system = Pacman.newRepositorySystem();
        RepositorySystemSession session = Pacman.newRepositorySystemSession(system);
        RemoteRepository repo = Pacman.newCentralRepository();
        Artifact artifact = new DefaultArtifact(coords);
        CollectRequest collectRequest = new CollectRequest();
        collectRequest.setRoot(new Dependency(artifact, ""));
        // collectRequest.addRepository(repo);
        CollectResult collectResult = system.collectDependencies(session, collectRequest);
        collectResult.getRoot().accept(new ConsoleDependencyGraphDumper());
        return getDependencyURLs(collectResult.getRoot());
    }

    private List<URL> getDependencyURLs(DependencyNode root) {
        try {
            RepositorySystem system = Pacman.newRepositorySystem();
            RepositorySystemSession session = Pacman.newRepositorySystemSession(system);
            String path = session.getLocalRepositoryManager().getPathForLocalArtifact(root.getDependency().getArtifact());
            List<URL> urls = new ArrayList<URL>();
            urls.add(new File(session.getLocalRepository().getBasedir(), path).toURI().toURL());
            for (DependencyNode node : root.getChildren()) {
                urls.addAll(getDependencyURLs(node));
            }
            return urls;
        } catch (Exception ex) {
            Logger.getLogger(MavenClassService.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }
    }

    @Override
    public Class get(final URI uri) {
        if (!uri.getScheme().equals("mvn")) {
            return null;
        }
        return AccessController.doPrivileged(new PrivilegedAction<Class>() {
            @Override
            public Class run() {
                try {
                    if (classLoader == null) {
                        artifactName = uri.getSchemeSpecificPart().substring(2);
                        String[] coords = artifactName.split(":");
                        List<URL> urls = null;
                        if (coords.length == 3) {
                            urls = getDependencyURLs(artifactName);
                            jarFileURL = urls.get(0);
                        } else if (coords.length == 2) {
                            // latest version
                            artifactName = artifactName + ":" + findNewestVersion(artifactName);
                            urls = getDependencyURLs(artifactName);
                            jarFileURL = urls.get(0);
                        }
                        classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]));
                    }

                    int i = uri.getSchemeSpecificPart().indexOf("/", 2);
                    String className = null;
                    if (i == -1) {
                        URL url = new URL("jar:" + jarFileURL.toString() + "!/");
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        className = jarURLConnection.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
                    } else {
                        className = uri.getSchemeSpecificPart().substring(i + 1).replace("/", ".").replace(".class", "");
                    }
                    if (className == null || className.isEmpty()) {
                        throw new RuntimeException("Invalid class name in URI!");
                    }
                    return Class.forName(className, true, classLoader);
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
            }
        });
    }

    @Override
    public String getName() {
        return "MavenClassService";
    }
}
